#include <iostream>
#include <fstream>
#include <cstdlib>
#define _USE_MATH_DEFINES
#include <math.h>
#include "option.h"
#include <algorithm>


using namespace std;
using namespace PMVS3;

Soption::Soption(void) {
    m_level = 1;          m_csize = 2;
    m_threshold = 0.7;    m_wsize = 7;
    m_minImageNum = 3;    m_CPU = 8;
    m_setEdge = 0.0f;     m_useBound = 0;
    m_useVisData = 0;     m_sequence = -1;
    m_tflag = -10;
    m_oflag = -10;


    // Max angle must be at least this big
    m_maxAngleThreshold = 10.0f * M_PI / 180.0f;
    // The smaller the tighter
    m_quadThreshold = 2.0f;
}  

void Soption::init(const std::string prefix, const std::vector<int> iamges,
                   const std::vector<int> oimages,std::vector<std::vector<double> > &vis) {
    m_prefix = prefix;
    m_tflag=iamges.size();
    m_oflag=oimages.size();

    if(iamges.size()==0)
        cerr<<"m_timages is not valid:"<<iamges.size()<<endl;
    for (int i = 0; i < iamges.size(); ++i) {
        m_timages.push_back(iamges[i]);
    }

    if(iamges.size()==0)
        cerr<<"m_oimages is not valid:"<<iamges.size()<<endl;

    for (int i = 0; i <oimages.size(); ++i)
        m_oimages.push_back(oimages[i]);



    if (m_tflag == -10 || m_oflag == -10) {
        cerr << "m_tflag and m_oflag not specified: "
             << m_tflag << ' ' << m_oflag << endl;
        exit (1);
    }

    //----------------------------------------------------------------------
    string sbimages = prefix + string("bimages.dat");

    for (int i = 0; i < (int)m_timages.size(); ++i)
        m_dict[m_timages[i]] = i;

    initOimages(vis);
    initVisdata();

    if (m_useBound)
        initBindexes(sbimages);

    cerr << "--------------------------------------------------" << endl
         << "--- Summary of specified options ---" << endl;

    cerr << "# of timages: " << (int)m_timages.size();
    cerr << "# of oimages: " << (int)m_oimages.size();

    cerr << "level: " << m_level << "  csize: " << m_csize << endl
         << "threshold: " << m_threshold << "  wsize: " << m_wsize << endl
         << "minImageNum: " << m_minImageNum << "  CPU: " << m_CPU << endl
         << "useVisData: " << m_useVisData << "  sequence: " << m_sequence << endl;
    cerr << "--------------------------------------------------" << endl;
}

void Soption::initOimages(const std::vector<std::vector<double> > &vis) {
    if (m_oflag != -2)
        return;

//    string svisdata = m_prefix + string("vis.dat");
//    ifstream ifstr;
//    ifstr.open(svisdata.c_str());
//    if (!ifstr.is_open()) {
//        cerr << "No vis.dat although specified to initOimages: " << endl
//             << svisdata << endl;
//        exit (1);
//    }

//    string header;  int num2;
//    ifstr >> header >> num2;

    m_oimages.clear();
    for (int c = 0; c < vis.size(); ++c) {
        int index0;
        map<int, int>::iterator ite0 = m_dict.find(c);
        if (ite0 == m_dict.end())
            index0 = -1;
        else
            index0 = ite0->second;
//        int itmp;
//        ifstr >> itmp >> itmp;
//        for (int i = 0; i < itmp; ++i) {
//            int itmp2;
//            ifstr >> itmp2;
//            if (index0 != -1 && m_dict.find(itmp2) == m_dict.end())
//                m_oimages.push_back(itmp2);
//        }
        for (int i = 1; i < vis[c].size(); ++i) {

            if (index0 != -1 && m_dict.find(vis[c][i]) == m_dict.end())
                m_oimages.push_back(vis[c][i]);
        }
    }
//    ifstr.close();

    sort(m_oimages.begin(), m_oimages.end());
    m_oimages.erase(unique(m_oimages.begin(), m_oimages.end()), m_oimages.end());
}

// When do not use vis.dat
void Soption::initVisdata(void) {
    // Case classifications. Set m_visdata by using vis.dat or not.
    if (m_useVisData == 0) {
        const int tnum = (int)m_timages.size();
        const int onum = (int)m_oimages.size();
        const int num = tnum + onum;
        m_visdata.resize(num);
        m_visdata2.resize(num);
        for (int y = 0; y < num; ++y) {
            m_visdata[y].resize(num);
            for (int x = 0; x < num; ++x)
                if (x == y)
                    m_visdata[y][x] = 0;
                else {
                    m_visdata[y][x] = 1;
                    m_visdata2[y].push_back(x);
                }
        }
    }
    else
        initVisdata2();
}

// Given m_timages and m_oimages, set m_visdata, m_visdata2
void Soption::initVisdata2(void) {
    string svisdata = m_prefix + string("vis.dat");

    vector<int> images;
    images.insert(images.end(), m_timages.begin(), m_timages.end());
    images.insert(images.end(), m_oimages.begin(), m_oimages.end());
    map<int, int> dict2;
    for (int i = 0; i < (int)images.size(); ++i)
        dict2[images[i]] = i;

    ifstream ifstr;
    ifstr.open(svisdata.c_str());
    if (!ifstr.is_open()) {
        cerr << "No vis.dat although specified to initVisdata2: " << endl
             << svisdata << endl;
        exit (1);
    }

    string header;  int num2;
    ifstr >> header >> num2;

    m_visdata2.resize((int)images.size());
    for (int c = 0; c < num2; ++c) {
        int index0;
        map<int, int>::iterator ite0 = dict2.find(c);
        if (ite0 == dict2.end())
            index0 = -1;
        else
            index0 = ite0->second;
        int itmp;
        ifstr >> itmp >> itmp;
        for (int i = 0; i < itmp; ++i) {
            int itmp2;
            ifstr >> itmp2;
            int index1;
            map<int, int>::iterator ite1 = dict2.find(itmp2);
            if (ite1 == dict2.end())
                index1 = -1;
            else
                index1 = ite1->second;

            if (index0 != -1 && index1 != -1)
                m_visdata2[index0].push_back(index1);
        }
    }
    ifstr.close();

    const int num = (int)images.size();
    m_visdata.clear();
    m_visdata.resize(num);
    for (int y = 0; y < num; ++y) {
        m_visdata[y].resize(num);
        fill(m_visdata[y].begin(), m_visdata[y].end(), 0);
        for (int x = 0; x < (int)m_visdata2[y].size(); ++x)
            m_visdata[y][m_visdata2[y][x]] = 1;
    }

    // check symmetry
    for (int i = 0; i < (int)m_visdata.size(); ++i) {
        for (int j = i+1; j < (int)m_visdata.size(); ++j) {
            if (m_visdata[i][j] != m_visdata[j][i]) {
                m_visdata[i][j] = m_visdata[j][i] = 1;
            }
        }
    }
}

void Soption::initBindexes(const std::string sbimages) {
    if (sbimages.empty())
        return;

    m_bindexes.clear();
    ifstream ifstr;
    ifstr.open(sbimages.c_str());
    if (!ifstr.is_open()) {
        cerr << "File not found: " << sbimages << endl;
        exit (1);
    }

    cerr << "Reading bimages" << endl;
    int itmp;
    ifstr >> itmp;
    for (int i = 0; i < itmp; ++i) {
        int itmp0;
        ifstr >> itmp0;

        if (m_dict.find(itmp0) != m_dict.end())
            m_bindexes.push_back(m_dict[itmp0]);
    }
    ifstr.close();
}
